/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under Ultimate Liberty license
  * SLA0044, the "License"; You may not use this file except in compliance with
  * the License. You may obtain a copy of the License at:
  *                             www.st.com/SLA0044
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "crc.h"
#include "dma.h"
#include "fatfs.h"
#include "spi.h"
#include "usart.h"
#include "usb_device.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "string.h"
#include "LED.h"
#include "MulTask.h"
#include "BootLoader.h"
#include "CPUFlash.h"
#include "exFatFs.h"
#include "W25QXX.h"
#include "SampleUI.h"
#include "KeyValue.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
static FRESULT BLT_FATFS_Builtin(void);
static FRESULT BLT_FATFS_Destuct(void);
/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
// #define MAIN_LOG(fmt, ...)  printf("[main.c|%d] "fmt"\r\n",__LINE__,##__VA_ARGS__)
#define  MAIN_LOG(fmt, ...)  SampleUI_Print(fmt"\n",##__VA_ARGS__)
#define APP_DIR_PATH      "0:/" //app文件存放目录，默认根目录
#define SETTING_DIR_PATH "0:/setting" //设置文件夹(内有一个设置文件和FATFS中文GB-UNICODE编码的转换文件)
/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
Task idle_task;
Task led_task;
Task app_check_task;
Task uart_task;
char app_name[32] = {0}; // app绝对路径
BLTAppFile app = {.fs_built = BLT_FATFS_Builtin,
                  .fs_destruct = BLT_FATFS_Destuct,
                  .bin_path = "0:/app.bin"}; 
                  //APP的默认名字为 根目录下 app.bin,但程序会搜索APP_DIR_PATH下的所有bin文件再选择最后一个bin作为运行文件
char oled_text_buff[256] = {0};
uint8_t blt_state = 0; //bootloader状态机，用于取消bootloader下载过程中的延时展示log的时间
char setting_file_buff[256] = {0};
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
void ALLPeri_DeInit(uint8_t para); //所有用到外设的复位函数
void IdleTask_CallBackFunc(void * p_context);
void APPCheck_CallBackFunc(void * p_context);
void UartTask_CallBackFunc(void * p_context);
void LedTask_CallBackFunc(void * p_context);
FRESULT MAIN_Flash_FATFS_Test(void);
FRESULT MAIN_Update_AppFileToSettingFile(void);
/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
  uint8_t operation_mode = 0; //0默认跳转到APP运行，1跳转到APP升级模式
  FRESULT res = FR_OK;
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  /* USER CODE BEGIN 2 */
  //判断按键模式选择
  if(HAL_GPIO_ReadPin(PAD_S_GPIO_Port,PAD_S_Pin) == GPIO_PIN_RESET) //如果PAD_S按下的话
      operation_mode = 1; //APP升级模式
  else
  {
      if(BLT_APP_Is_Exist())operation_mode = 0; //APP运行模式
      else {operation_mode = 1;}//app不存在的话，还是升级模式
  }
  LED_OFF(BLUE);
  LED_OFF(GREEN);
  if(operation_mode) //升级模式
  {
      MX_SPI1_Init();
      OLED_Init(); //屏幕初始化，显示调试信息
      SampleUI_Init(oled_text_buff,sizeof(oled_text_buff),8); //8号字体终端显示
      MX_SPI2_Init(); //W25QXX初始化
      MX_USART1_UART_Init(); //串口初始化
      MX_FATFS_Init(); //文件系统初始化
      MAIN_LOG("BootLoader Mode");
      //设置卷标
      for(uint8_t i = 0;i < 5;i++) //开始先读5次容量大小，确保Flash和FATFS是正常的
      {
          res = MAIN_Flash_FATFS_Test();
          if(res == FR_OK)break;
      }
      res = f_mount(&USERFatFS,(const TCHAR *)"0:/",1);
      if(res == FR_OK)
      {//挂载成功
          //设置卷标
          char vol_name[32] = {0};
          uint32_t vsn = 0;
          FIL setting_file;
          UINT br;
          MAIN_LOG("mount OK");
          res = f_getlabel((TCHAR *)"0:/",(TCHAR *)vol_name,(DWORD*)&vsn);
          MAIN_LOG("vol_name = %s\n"
                    "res = %d",vol_name,res);
          if(strncmp(vol_name,"BOOT",4) != 0)
          {//卷标不是BOOT就设置为Boot
              f_setlabel("Boot");
              MAIN_LOG("set vol_name BOOT");
          }
          //读取配置文件
          res = f_open(&setting_file,SETTING_DIR_PATH"/setting.txt",FA_READ);
          if(res == FR_OK)
          {
              Dict dict;
              uint8_t set_read = 0; //配置匹配进度
              eDICT_ERR ret = eDICT_ERR_OK;
              char temp[32] = {0};
              f_read(&setting_file,&setting_file_buff,sizeof(setting_file_buff),&br);
              Dict_Init(&dict);
              ret = Dict_Loads(&dict,setting_file_buff);
              if(ret == eDICT_ERR_OK)
              {
                  printf("%s",setting_file_buff);
                  if(eDICT_ERR_OK == Dict_GetValue(&dict,"oled_inv",temp))
                  {
                      set_read++;
                      if(0 == strcmp(temp,"1"))
                          {OLED_DisplayTurn(1); MAIN_LOG("oled_inv=1");}//反转显示
                      else if(0 == strcmp(temp,"0"))
                          {OLED_DisplayTurn(0); MAIN_LOG("oled_inv=0");}
                  }
                  if(eDICT_ERR_OK == Dict_GetValue(&dict, "appfile", temp))
                  {
                      set_read++;
                      MAIN_LOG("proc_app=%s",temp);
                  }
              }
              if(set_read>=2) //两个配置都读出来了(或者文件读完了)
              {
                  MAIN_LOG("config ok%d",set_read);
              }
              Dict_Clears(&dict);
          }else {MAIN_LOG("Setting file not exist");}
          res = f_close(&setting_file);
      } 
      else {MAIN_LOG("mount fail\nW25QXX Error\nres = %d",res);}
      res = f_mount(NULL, (const TCHAR *)"0:/" , 1); //立即卸载文件系统;
      //USB设备初始化
      MX_USB_DEVICE_Init(); 
      exFatFs_Proc(APP_DIR_PATH,BLT_FATFS_Builtin,BLT_FATFS_Destuct,app_name);
      exFatFs_Print();
      MAIN_LOG("new_app = %s",app_name);
      LED_ON(BLUE); //蓝灯提示，升级模式
      MulTask_Init(&idle_task, IdleTask_CallBackFunc, NULL);
      MulTask_TaskInit(&uart_task, 20, UartTask_CallBackFunc, NULL);
      MulTask_TaskInit(&led_task, 1000, LedTask_CallBackFunc, NULL);
      MulTask_TaskInit(&app_check_task, 4000, APPCheck_CallBackFunc, NULL);
      /* USER CODE END 2 */

      /* Infinite loop */
      /* USER CODE BEGIN WHILE */
      while (1)
      {
        /* USER CODE END WHILE */

        /* USER CODE BEGIN 3 */
        MulTask_Loop();
      }
      /* USER CODE END 3 */
  }
  else
  {
      LED_ON(GREEN); //绿灯提示，APP运行模式
      BLT_JumpToApp(ALLPeri_DeInit,0);//跳转APP运行(没有用到usb)
  }



 
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USB;
  PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_PLL_DIV1_5;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */
void ALLPeri_DeInit(uint8_t para) //所有用到外设的复位函数
{
    if(para)
    {//如果para=1,则表明是从Bootloader模式跳转，需要额外deinit bootloader所使用的硬件
      MX_USB_DEVICE_DeInit(); 
      HAL_SPI_MspDeInit(&hspi2);
      HAL_UART_MspDeInit(&huart1);
      HAL_SPI_MspDeInit(&hspi1);
    }
    HAL_GPIO_WritePin(GPIOC, LED_BLUE_Pin|LED_GREEN_Pin, GPIO_PIN_SET); //关掉灯
    HAL_GPIO_WritePin(GPIOB, OLED_RES_Pin|OLED_DC_Pin, GPIO_PIN_RESET);
    HAL_GPIO_DeInit(GPIOA,GPIO_PIN_All);
    HAL_GPIO_DeInit(GPIOB,GPIO_PIN_All);
    HAL_GPIO_DeInit(GPIOC,GPIO_PIN_All);
    HAL_GPIO_DeInit(GPIOD,GPIO_PIN_All);
    //复位所有IO
    //关闭所有IO的时钟
    __HAL_RCC_GPIOA_CLK_DISABLE();
    __HAL_RCC_GPIOB_CLK_DISABLE();
    __HAL_RCC_GPIOC_CLK_DISABLE();
    __HAL_RCC_GPIOD_CLK_DISABLE();
}

static FRESULT BLT_FATFS_Builtin(void)
{
    return f_mount(&USERFatFS,(const TCHAR *)"0:/",1);
}
static FRESULT BLT_FATFS_Destuct(void)
{
    return f_mount(NULL, (const TCHAR *)"0:/" , 1);
}

void IdleTask_CallBackFunc(void * p_context)
{
}
void LedTask_CallBackFunc(void * p_context)
{
    LED_TOGGLE(BLUE); //等待升级的时候红灯闪烁
}
void APPCheck_CallBackFunc(void * p_context)
{
    FRESULT ret = FR_OK;
    switch (blt_state)
    {
        case 0: //搜索可执行文件  每4s一次
            exFatFs_Proc(APP_DIR_PATH,BLT_FATFS_Builtin,BLT_FATFS_Destuct,app_name);
            if(strlen(app_name) != 0) //搜索到了
            {
                blt_state = 1; //进入下一个阶段
                app_check_task.limit = 800; //改为800ms时间间隔
            }
        break;
        case 1: //获取文件信息
            app.bin_path = app_name; //将文件名赋值进去
            ret = BLT_GetAppInfo(&app); //获取bin文件的信息
            if(ret == FR_OK)
            {
                blt_state++;
                MAIN_LOG("Get APP info");
            }
            else 
            {
                MAIN_LOG("Get APP info fail%d",ret);
                blt_state = 0;
                app_check_task.limit = 2000;
            }
        break;
        case 2:
            ret = BLT_GetAppCRC(&app); //获取bin文件CRC值
            if(FR_OK == ret)
            {
                if (app.bin_crc != app.bin_cal_crc)
                {
                    MAIN_LOG("CRC fail");
                    MAIN_LOG("CRC = 0x%x",app.bin_crc);
                    MAIN_LOG("CAL CRC = 0x%x",app.bin_cal_crc);
                    blt_state = 0;
                    app_check_task.limit = 2000;
                }
                else
                {
                  MAIN_LOG("CRC correct\nloading app");
                  MAIN_LOG("===================");
                  blt_state++;
                }
            }
        break;
        case 3:
            ret = BLT_LoadApp(&app); //将app文件load到flash
            if(FR_OK == ret)
            {
                MAIN_LOG("===================");
                MAIN_LOG("jump to app");
                blt_state++;  
            }
            else
            { 
                MAIN_LOG("Loading failed");
                blt_state = 0;
                app_check_task.limit = 2000;
            }
        break;
        case 4: //接跳转app不用返回了
            //在跳转前需要将运行文件的名字写入setting/setting文件
            {
              W25QXX_Wait_Busy(0xFFFFFFFF); //等待上面的app完全加载结束
              MAIN_Update_AppFileToSettingFile();
              W25QXX_Wait_Busy(0xFFFFFFFF); //flash写入完成才跳转
              BLT_JumpToApp(ALLPeri_DeInit,1);//跳转APP运行(用到了usb)
            }
        break;
        default:break;
    }
}
void UartTask_CallBackFunc(void * p_context)
{
  static uint8_t temp = 0;
  FRESULT res = FR_OK;
  DIR dir;
  FILINFO fno;
  if(HAL_UART_Receive_IT(&huart1,&temp,1) == HAL_OK)
  {
      // MAIN_LOG("recv = :%c",temp);
      switch (temp)
      {
        case 'a':
            {
              exFatFs_Proc(APP_DIR_PATH,BLT_FATFS_Builtin,BLT_FATFS_Destuct,app_name);
              exFatFs_Print();
              app.bin_path = app_name; //将文件名赋值进去
              MAIN_LOG("proc_app_name = %s,len = %d",app_name,strlen(app_name));
            }
          break;
        case 'b':
            BLT_GetAppInfo(&app);
            break;
        case 'c':
            BLT_GetAppCRC(&app);
            break;
        case 'd':
            BLT_LoadApp(&app);
            break;
        case 'e':
            if (app.bin_crc != app.bin_cal_crc)
            {
                MAIN_LOG("CRC fail");
                MAIN_LOG("CRC = 0x%x",app.bin_crc);
                MAIN_LOG("CAL CRC = 0x%x",app.bin_cal_crc);
            }
            else
            {
              MAIN_LOG("CRC successful\n jump to app");
              MAIN_LOG("==================");
              BLT_JumpToApp(ALLPeri_DeInit,1);
            }
          break;
        case 'f':
              BLT_JumpToApp(ALLPeri_DeInit,1);
            break;
        case 'g':
            res = f_mount(&USERFatFS,(const TCHAR *)"0:/",1);
            if(res == FR_OK){MAIN_LOG("mount OK");} //挂载成功
            res = f_opendir(&dir,"0:/");
            if(res == FR_OK)
            {
              MAIN_LOG("open dir OK");
              while(f_readdir(&dir,&fno) == FR_OK && fno.fname[0] != 0)
              {
                MAIN_LOG("file name = %s",fno.fname);
                int hour = 0, minute = 0,sec = 0;
                int year = 0, month = 0,day = 0;
                exFatFs_fdate2yyyymmdd(fno.fdate,&year,&month,&day);
                exFatFs_ftime2hhmmss(fno.ftime,&hour,&minute,&sec);
                MAIN_LOG("file date = %d-%d-%d",year,month,day);
                MAIN_LOG("file time = %d:%d:%d",hour,minute,sec);
              }
            }
            f_mount(NULL, (const TCHAR *)"0:/" , 1); //立即卸载文件系统
            break;
        case 'h':
            MAIN_Update_AppFileToSettingFile();
        break;
        
        case 'i':
        {
            FIL setting_file;
            Dict dict;
            UINT br=0;
            eDICT_ERR ret = eDICT_ERR_OK;
            char temp[32] = {0};
            BLT_FATFS_Builtin();
            f_read(&setting_file,&setting_file_buff,sizeof(setting_file_buff)-1,&br);
            Dict_Init(&dict);
            ret = Dict_Loads(&dict,setting_file_buff);
            if(ret == eDICT_ERR_OK)
            {
                printf("%s",setting_file_buff);
                if(eDICT_ERR_OK == Dict_GetValue(&dict,"oled_inv",temp))
                {
                    if(0 == strcmp(temp,"1"))
                        {OLED_DisplayTurn(1); MAIN_LOG("oled_inv=1");}//反转显示
                    else if(0 == strcmp(temp,"0"))
                        {OLED_DisplayTurn(0); MAIN_LOG("oled_inv=0");}
                }
                if(eDICT_ERR_OK == Dict_GetValue(&dict, "appfile", temp))
                {
                    MAIN_LOG("appfile=%s",temp);
                }
            }
            Dict_Clears(&dict);
            f_close(&setting_file);
            BLT_FATFS_Destuct();
        }
        break;
        case 'j':
        {
            W25QXX_Erase_Chip();
        }
        break;
        default:  break;
      }
  }
}

FRESULT MAIN_Flash_FATFS_Test(void)
{
    FRESULT res = FR_OK;
    uint32_t total_size = 0, free_size = 0;
    res = f_mount(&USERFatFS,(const TCHAR *)"0:/",1);
    if(res == FR_OK){MAIN_LOG("mount OK");} //挂载成功
    else {MAIN_LOG("mount failed");return res;}
    res = fatfs_get_info((const TCHAR *)"0:/",&total_size,&free_size);
    if(res == FR_OK)MAIN_LOG("get size OK\ntotal=%d,free= %d",total_size,free_size);
    else {MAIN_LOG("get size err = %d",res);return res;}
    f_mount(NULL, (const TCHAR *)"0:/" , 1); //立即卸载文件系统
    return res;
}

FRESULT MAIN_Update_AppFileToSettingFile(void)
{
    FIL setting_file;
    Dict dict;
    UINT bw=0;
    FRESULT fr = FR_OK;
    printf("[%d]%s\r\n",__LINE__,app.bin_path);
    BLT_FATFS_Builtin();
    fr = f_open(&setting_file,SETTING_DIR_PATH"/setting.txt",FA_READ);
    if(FR_OK == fr)
    {
        Dict_Init(&dict);
        memset(setting_file_buff,0,sizeof(setting_file_buff));
        f_read(&setting_file,setting_file_buff, sizeof(setting_file_buff)-1, &bw); //先读出配置
        printf("[%d]%s\r\n",__LINE__,setting_file_buff);
        Dict_Loads(&dict,setting_file_buff);
        Dict_SetValue(&dict,"appfile",app.bin_path);
        f_close(&setting_file);
        fr = f_open(&setting_file, SETTING_DIR_PATH"/setting.txt", FA_WRITE);
        {
            memset(setting_file_buff,0,sizeof(setting_file_buff));
            Dict_Dumps(&dict,setting_file_buff);
            printf("[%d]%s\r\n",__LINE__,setting_file_buff);
            fr = f_write(&setting_file, setting_file_buff, sizeof(setting_file_buff)-1, &bw);
            if (fr != FR_OK) {
                printf("f_write failed: %d\r\n", fr);
            } else if (bw != strlen(setting_file_buff)) {
                printf("f_write incomplete: %u bytes written\r\n", bw);
            }
            Dict_Clears(&dict);
            f_sync(&setting_file);
            f_close(&setting_file);
        }
    }
    BLT_FATFS_Destuct();
    return fr;
}

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
